<?php

namespace App\Service\Kms\Task\Common;

use App\Error\Kms\KmsTaskError;
use App\Events\Kms\TaskNodeFinishEvent;
use App\Exceptions\KfHttpResponseException;
use App\Models\Kms\Task\TaskModel;
use App\Models\Kms\Task\TaskNodesModel;
use App\Models\Kms\Task\TaskNodesProcessorModel;
use App\Models\Kms\Task\TaskTemplateModel;
use App\Models\Kms\Task\TaskTemplateNodesModel;
use App\Models\Kms\Task\TaskTemplateNodesProcessorModel;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;

class TaskService
{

    public TaskTemplateModel $taskTemplateModel;
    public TaskModel         $taskModel;

    /**
     * @param TaskModel $taskModel
     * @return $this
     */
    public function setTaskModel(TaskModel $taskModel): TaskService
    {
        $this->taskModel = $taskModel;
        return $this;
    }

    /**
     * @param TaskTemplateModel $taskTemplateModel
     * @param int $businessId
     * @param int $uid
     * @return TaskModel|Builder|Model
     * @throws \Exception
     */
    public function generateTask(TaskTemplateModel $taskTemplateModel, int $businessId, int $uid)
    {
        $this->taskTemplateModel = $taskTemplateModel;
        $this->taskModel         = TaskModel::query()->create([
            "businessId"   => $businessId,
            "templateId"   => $this->taskTemplateModel->id,
            "status"       => TaskModel::TASK_STATUS_ING,
            "createdAt"    => time(),
            "createdByUid" => $uid
        ]);

        $this->nextNode($this->taskModel, $uid);
        return $this->taskModel;
//        $this->generateNextNode(34);
//        $this->generateNextNode(13);
//        $this->generateNextNode(14, TaskNodesProcessorModel::STATUS_REJECT);
//        $this->generateNextNode(13);
////        $this->generateNextNode($uid, self::APPROVAL_RESULT_REJECT);
////        $this->generateNextNode($uid);
//        $this->generateNextNode(19, TaskNodesProcessorModel::STATUS_PASS);
    }

    /**
     * @param TaskModel $taskModel
     * @param int $uid
     * @param int $approvalResult
     * @param string $remark
     * @param array $extra
     * @throws \Exception
     */
    public function nextNode(
        TaskModel $taskModel,
        int $uid,
        array $extra = [],
        int $approvalResult = TaskNodesModel::BUSINESS_STATUS_FINISHED,
        string $remark = ""
    ) {
        $this->taskTemplateModel = $taskModel->templateRel;
        $this->taskModel         = $taskModel;
        $this->generateNextNode($uid, $approvalResult, $remark, $extra);
    }

    /**
     * 生成下一下节点
     *
     * @param int $uid
     * @param int $approvalResult
     * @param string $remark
     * @param array $extra
     * @throws \Exception
     */
    private function generateNextNode(
        int $uid,
        int $approvalResult = TaskNodesModel::BUSINESS_STATUS_FINISHED,
        string $remark = "",
        array $extra = []
    ) {
        $taskNodes          = $this->taskModel->refresh()->taskNodesRel;
        $isApprovalRejected = TaskNodesModel::IS_APPROVAL_REJECTED_NO;
//        $rejectApprovalData = [];

        if ($taskNodes->isEmpty()) {
            //未有节点信息 直接创建第一个节点
            $templateNowNode = $this->taskTemplateModel->templateNodesRel->first();
        } else {
            $nowNodes = $taskNodes->last();
            //节点处理状态变更
            $nowNodesProcessorModel = $nowNodes->nodesProcessorRel->where("handlerUserId", $uid)->first();
            if (empty($nowNodesProcessorModel)) {
                throw new KfHttpResponseException(KmsTaskError::HAVE_NO_PERMISSION_TO_OPERATE_THE_CURRENT_TASK_NODE);
            }
            //节点处理人完成节点任务
            (clone $nowNodesProcessorModel)->setRawAttributes([
                "remark"       => $remark,
                "extra"        => json_encode($extra, JSON_UNESCAPED_UNICODE),
                "status"       => $approvalResult,
                "updatedByUid" => $uid,
                "updatedAt"    => time()
            ])->save();
            //标记当前节点任务为已完成
            (clone $nowNodes)->setRawAttributes([
                "businessStatus"     => $approvalResult,
                "businessStatusText" => $this->getNodeEnumValText(
                    $approvalResult,
                    $nowNodes->isApprovalRejected,
                    $this->taskTemplateModel->templateNodesRel->where("step", $nowNodes->step)->first()
                ),
                "updatedByUid"       => $uid,
                "updatedAt"          => time()
            ])->save();


            //获取下一节点step
            $nowStep = $nowNodes->step;
            if ($nowNodes->businessType == TaskTemplateNodesModel::BUSINESS_TYPE_APPROVE && $approvalResult == TaskNodesModel::BUSINESS_STATUS_REJECT) {
                $nextStep           = $this->taskTemplateModel->templateNodesRel->where("step", $nowStep)
                                                                                ->first()->preStep;
                $isApprovalRejected = TaskNodesModel::IS_APPROVAL_REJECTED_YES;
            } else {
                $nextStep = $nowStep + 1;
            }

            //获取下一节点模板信息
            $templateNowNode = $this->taskTemplateModel->templateNodesRel->where("step", $nextStep)->first();

            //节点完成事件触发
            event(new TaskNodeFinishEvent($nowNodes->taskId, $nowNodes->id));
        }

        //生成下一节点任务
        $nowTaskNodeModel = TaskNodesModel::query()->create(array_merge(
            $templateNowNode->only(["businessType", "businessTypeText", "handleType", "approveType", "step"]), [
                "isApprovalRejected" => $isApprovalRejected,
                "businessStatusText" => $this->getNodeEnumValText(
                    TaskNodesModel::BUSINESS_STATUS_STAY,
                    $isApprovalRejected,
                    $templateNowNode
                ),
                "taskId"             => $this->taskModel->id,
                "businessStatus"     => TaskNodesModel::BUSINESS_STATUS_STAY,
                "createdAt"          => time(),
                "createdByUid"       => $uid
            ]
        ));

        //生成节点处理人信息
        TaskNodesProcessorModel::query()->insert($templateNowNode->nodesProcessorRel->map(function (
            TaskTemplateNodesProcessorModel $taskTemplateNodesProcessorModel
        ) use ($uid, $nowTaskNodeModel) {
            return array_merge($taskTemplateNodesProcessorModel->only([
                "handlerUserType",
                "handlerUserId",
                "handlerUsername"
            ]), [
                "nodeId"       => $nowTaskNodeModel->id,
                "createdAt"    => time(),
                "createdByUid" => $uid
            ]);
        })->values()->toArray());


        //最后一步流程变更已完成
        if ($nowTaskNodeModel->businessType == TaskTemplateNodesModel::BUSINESS_TYPE_FINISHED) {
            //节点处理人状态变更
            TaskNodesProcessorModel::query()
                                   ->whereIn("id", $nowTaskNodeModel->nodesProcessorRel->pluck("id")->toArray())
                                   ->update([
                                       "status"    => TaskNodesModel::BUSINESS_STATUS_FINISHED,
                                       "updatedAt" => time()
                                   ]);

            //节点状态变更
            (clone $nowTaskNodeModel)->setRawAttributes([
                "businessStatus"     => TaskNodesModel::BUSINESS_STATUS_FINISHED,
                "businessStatusText" => $this->getNodeEnumValText(
                    TaskNodesModel::BUSINESS_STATUS_FINISHED,
                    $isApprovalRejected,
                    $templateNowNode
                ),
                "updatedByUid"       => $uid,
                "updatedAt"          => time()
            ])->save();

            //任务状态变更
            (clone $this->taskModel)->setRawAttributes([
                "status"       => TaskModel::TASK_STATUS_FINISHED,
                "updatedByUid" => $uid,
                "updatedAt"    => time()
            ])->save();

            //节点完成事件触发
            event(new TaskNodeFinishEvent($nowTaskNodeModel->taskId, $nowTaskNodeModel->id));
        }
    }

    /**
     * @param int $businessStatus
     * @param int $isApprovalRejected
     * @param TaskTemplateNodesModel $taskTemplateNodesModel
     * @return mixed
     */
    public function getNodeEnumValText(
        int $businessStatus,
        int $isApprovalRejected,
        TaskTemplateNodesModel $taskTemplateNodesModel
    ) {
        $enumJson = $isApprovalRejected ? $taskTemplateNodesModel->rejectBusinessStatusEnum : $taskTemplateNodesModel->businessStatusEnum;
        return attributeGet(collect(jsonToArr($enumJson, []))->where("status", $businessStatus)->first(), "text", "-");
    }

    /**
     * 作废任务
     *
     * @param TaskModel $taskModel
     * @param int $uid
     * @param string $username
     */
    public function invalid(TaskModel $taskModel, int $uid, string $username)
    {
        $this->taskModel = $taskModel;

        //任务节点增加作废节点
        $nowTaskNodeModel = TaskNodesModel::query()->create([
            "businessType"       => TaskTemplateNodesModel::BUSINESS_TYPE_INVALID,
            "businessTypeText"   => TaskTemplateNodesModel::BUSINESS_TYPE_TEXT[TaskTemplateNodesModel::BUSINESS_TYPE_INVALID],
            "taskId"             => $this->taskModel->id,
            "businessStatus"     => TaskNodesModel::BUSINESS_STATUS_INVALID,
            "businessStatusText" => TaskNodesModel::BUSINESS_STATUS_TEXT[TaskNodesModel::BUSINESS_STATUS_INVALID],
            "createdAt"          => time(),
            "createdByUid"       => $uid,
            "updatedByUid"       => $uid
        ]);

        //生成节点处理人信息
        TaskNodesProcessorModel::query()->insert([
            "handlerUserId"   => $uid,
            "handlerUsername" => $username,
            "nodeId"          => $nowTaskNodeModel->id,
            "createdAt"       => time(),
            "createdByUid"    => $uid,
            "updatedByUid"    => $uid,
            "status"          => TaskNodesModel::BUSINESS_STATUS_INVALID
        ]);

        //作废任务主表
        $taskModel->setRawAttributes([
            "status"       => TaskModel::TASK_STATUS_INVALID,
            "updatedAt"    => time(),
            "updatedByUid" => $uid
        ])->save();

        //节点完成事件触发
        event(new TaskNodeFinishEvent($nowTaskNodeModel->taskId, $nowTaskNodeModel->id));
    }

    /**
     * @param TaskModel $taskModel
     * @return mixed
     */
    public static function getTaskProgress(TaskModel $taskModel)
    {
        return $taskModel->taskNodesRel->map(function (TaskNodesModel $taskNodesModel) {
            return [
                "step"                   => $taskNodesModel->step,
                "businessType"           => $taskNodesModel->businessType,
                "businessTypeText"       => $taskNodesModel->businessTypeText,
                "businessStatus"         => $taskNodesModel->businessStatus,
                "businessStatusText"     => $taskNodesModel->businessStatusText,
                "isApprovalRejected"     => $taskNodesModel->isApprovalRejected,
                "isApprovalRejectedText" => TaskNodesModel::IS_APPROVAL_REJECTED_TEXT[$taskNodesModel->isApprovalRejected] ?? "",
                "handleType"             => $taskNodesModel->handleType,
                "approveType"            => $taskNodesModel->approveType,
                "createdAt"              => $taskNodesModel->createdAt->format("Y-m-d H:i:s"),
                "createdByUid"           => $taskNodesModel->createdByUid,
                "updatedAt"              => $taskNodesModel->updatedAt->format("Y-m-d H:i:s"),
                "updatedByUid"           => $taskNodesModel->updatedByUid,
                "handleUserInfo"         => $taskNodesModel->nodesProcessorRel->map(function (
                    TaskNodesProcessorModel $taskNodesProcessorModel
                ) {
                    return [
                        "status"          => $taskNodesProcessorModel->status,
                        "statusText"      => TaskNodesModel::BUSINESS_STATUS_TEXT[$taskNodesProcessorModel->status] ?? "",
                        "remark"          => $taskNodesProcessorModel->remark,
                        "extra"           => jsonToArr($taskNodesProcessorModel->extra, []),
                        "handlerUserId"   => $taskNodesProcessorModel->handlerUserId,
                        "handlerUsername" => $taskNodesProcessorModel->handlerUsername,
                    ];
                })->values()->toArray()
            ];
        })->values()->toArray();
    }


    /**
     * 校验是否有处理权限
     *
     * @param TaskModel $taskModel
     * @param int $uid
     * @return bool
     */
    public static function checkUserPower(TaskModel $taskModel, int $uid): bool
    {
        $nowNode = $taskModel->taskNodesRel->last();
        return $taskModel->status == TaskModel::TASK_STATUS_ING
            &&
            in_array($uid, $nowNode->nodesProcessorRel->pluck("handlerUserId")->toArray());
    }

    /**
     * @param TaskModel $taskModel
     * @param int $businessType
     * @return bool
     */
    public static function checkNode(TaskModel $taskModel, int $businessType): bool
    {
        $nowNode = $taskModel->taskNodesRel->last();
        return $nowNode->businessType == $businessType;
    }

    /**
     * 操作校验
     *
     * @param TaskModel $taskModel
     * @param int $businessType
     * @param int $uid
     */
    public static function operateValidate(TaskModel $taskModel, int $businessType, int $uid)
    {

        //判断节点业务类型
        if (!self::checkNode($taskModel, $businessType)) {
            throw new KfHttpResponseException(KmsTaskError::THE_OPERATION_ON_THE_CURRENT_TASK_NODE_IS_INVALID);
        }

        //判断节点操作权限
        if (!self::checkUserPower($taskModel, $uid)) {
            throw new KfHttpResponseException(KmsTaskError::HAVE_NO_PERMISSION_TO_OPERATE_THE_CURRENT_TASK_NODE);
        }
    }
}
